package com.gitee.yongzhuzl.commonutil.util.file;

import com.gitee.yongzhuzl.commonutil.util.map.MapUtil;
import org.apache.commons.io.IOUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.Map;

/**
 * description: 文件下载工具类
 * @author 朱正磊
 * @date 2019年5月10日 下午4:25:53
 */
public class FileDownloadUtil {

    private static final Logger log = LoggerFactory.getLogger(FileDownloadUtil.class);
	
	/**
     * description: 将用户提交的表单信息下载成我们指定的文件
     * @param request 请求对象
     * @param response 响应对象
     * @param fileName 指定下载生成的文件名，比如：a.ini
     */
    public static void downloadForm(HttpServletRequest request, HttpServletResponse response, String fileName) {
        // 表单提交的数据
        Map<String, Object> queryParams = MapUtil.getQueryParams(request);
        // 转换成JSON对象
        JSONObject json = new JSONObject(queryParams);
        StringBuilder sb;
        try {
            // 遍历JSON，将JSON的值放至缓冲区
            sb = new StringBuilder();
            Iterator<?> it = json.keys();
            while (it.hasNext()) {
                String key =  (String) it.next();
                String value = (String) json.get(key);
                sb.append(key).append("=").append(value).append("\n");
            }
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }

        // 设置文件ContentType类型，这样设置，会自动判断下载文件类型
        response.setContentType("application/multipart/form-data");

        // 设置文件头：最后一个参数是设置下载文件名(假如我们叫a.ini)
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);

        try {
            // 用流将数据写给前端
            OutputStream os = response.getOutputStream();
            os.write(sb.toString().getBytes());
            os.flush();
            os.close();
        } catch (Exception e) {
            log.error("将用户提交的表单信息下载成我们指定的文件异常：", e);
        }
    }

    /**
     * description: 从服务器上下载文件
     * @param response 响应对象
     * @param filePath 文件在服务器上存储的绝对路径，比如：/home/a.txt
     */
    public static void downloadFile(HttpServletResponse response, String filePath) {
        // 获取服务器文件
        File file = null;
        InputStream ins = null;
        try {
            file = new File(filePath);
            ins = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            log.error(e.getMessage(), e);
        }

        // 设置文件ContentType类型，这样设置，会自动判断下载文件类型
        response.setContentType("multipart/form-data");

        // 设置文件头：最后一个参数是设置下载文件名
        response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());
        try {
            OutputStream os = response.getOutputStream();
            byte[] b = new byte[1024];
            int len;
            assert ins != null;
            while((len = ins.read(b)) > 0) {
                os.write(b, 0, len);
            }
            os.flush();
            os.close();
            ins.close();
        } catch (Exception e) {
            log.error("从服务器上下载文件异常：", e);
        }
    }

    /**
     * description: 根据url路径下载文件到本地
     * @param urlString 文件访问url地址，比如：http://*******:8080/picService/resLogoPic/93694182a2ad4778933fa15aa2fe44c0.jpg
     * @param filename 文件名，比如：93694182a2ad4778933fa15aa2fe44c0.jpg
     * @param savePath 文件存储路径，比如：H:\\
     */
    public static void downloadByUrl(String urlString, String filename, String savePath) {
        try {
            // 构造URL
            URI uri = new URI(urlString);
            URL url = uri.toURL();
            // 打开连接
            URLConnection con = url.openConnection();
            // 设置请求超时为5s
            con.setConnectTimeout(5 * 1000);
            // 输入流
            InputStream is = con.getInputStream();
            // 1K的数据缓冲
            byte[] bs = new byte[1024];
            // 读取到的数据长度
            int len;
            // 输出的文件流
            File sf = new File(savePath);
            if (!sf.exists()) {
                boolean flag = sf.mkdirs();
                if (!flag) {
                    throw new RuntimeException("创建文件夹失败！");
                }
            }
            OutputStream os = Files.newOutputStream(Paths.get(sf.getPath() + "\\" + filename));
            // 开始读取
            while ((len = is.read(bs)) != -1) {
                os.write(bs, 0, len);
            }
            // 完毕，关闭所有连接
            os.close();
            is.close();
        } catch (Exception e) {
            log.error("根据url路径下载文件到本地异常：", e);
        }
    }

    /**
     * description: 下载项目中资源文件夹下的静态文件
     * @author 朱正磊
     * @date 2023-10-18 09:39:52
     * @param response HTTP响应对象
     * @param filePath 文件相对路径（resources下的路径，比如：static/css/style.css）
     * @param clazz 项目中类的字节码对象
     */
    public static void downloadFile(HttpServletResponse response, String filePath, Class<?> clazz) {
        try {
            String fileName = getFileNameByFilePath(filePath, clazz);
            InputStream inputStream = clazz.getClassLoader().getResourceAsStream(filePath);
            response.setContentType("application/vnd.ms-excel;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            ServletOutputStream outputStream = response.getOutputStream();
            assert inputStream != null;
            IOUtils.copy(inputStream, outputStream);
            outputStream.flush();
            outputStream.close();
            inputStream.close();
        } catch (Exception e) {
            log.error("下载项目中资源文件夹下的静态文件异常：", e);
        }
    }

    /**
     * description: 根据项目中资源文件夹下的静态文件相对路径获取文件名
     * @author 朱正磊
     * @date 2025-02-20 14:54:16
     * @param filePath 文件相对路径（resources下的路径，比如：static/css/style.css）
     * @param clazz 项目中类的字节码对象
     * @return 返回文件名，比如：style.css
     */
    public static String getFileNameByFilePath(String filePath, Class<?> clazz) {
        URL resource = clazz.getClassLoader().getResource(filePath);
        assert resource != null;
        String[] pathArray = resource.getPath().split("/");
        return pathArray[pathArray.length - 1];
    }

}
